#include "MapController.h"

//Constructor
Map::Map()
{
	m_map = NULL ;
	m_width = m_height = 0 ;
	srand(unsigned(time(NULL))) ;
}

//Copy Constructor
Map::Map( Map & a_original )
{
	m_width =  a_original.m_width ;
	m_height = a_original.m_height ;

	m_map = new2Dmap( m_height, m_width ) ;

	for( int row = 0; row < m_height; ++row )
	{
		for( int col = 0; col < m_width; ++col )
		{
			m_map[row][col] = a_original.m_map[row][col] ;
		}
	}

	m_offset = 0 ;
}

//Loads the source file of the map, returns false if the file is not found
bool Map::loadFile( char * filename )
{
	fstream file(filename) ;

	//return false if the file is not found
	if( !file ) 
	{
		return false ;
	}
	else
	{
		//extract the width and height of the map from the file
		file >> m_width ;
		file >> m_height ;

		//create a dynamically allocated array of chars for drawing the map data
		m_map = new char * [m_height] ;
		m_hidden = new bool * [m_height] ;
		m_flagged = new bool * [m_height] ;
			
		//The file tends to include the space from the width and height numbers at the top
		//so for the sake of sanity, this moves where the file is being looked at to the next line in the file
		//where the real char data is held
		if( m_width > SIZE_EASY )
		{
			if( m_width == SIZE_NORMAL )
			{
				m_mineAmount = 60 ;
			}
			else
			{
				m_mineAmount = 100 ;
			}

			file.seekg(FILE_OFFSET_LARGE, ios::beg) ; //Map file
		}
		else
		{
			m_mineAmount = 10 ;
			file.seekg(FILE_OFFSET_SMALL, ios::beg) ; //Piece file
		}

		m_offset = ((float)SCREEN_WIDTH / 2.0f) - ((float)m_width/2.0f * 32.0f) ;
		//Get all the chars!
		for( int y = 0; y < m_height; ++y )
		{
			m_map[y] = new char[m_width] ;
			m_hidden[y] = new bool[m_width] ;
			m_flagged[y] = new bool [m_width] ;

			for( int x = 0; x < m_width; ++x )
			{
				m_map[y][x] = file.get() ;
			}		
			//Get the invisible '\n' at the end of each line
			file.get() ;
		}

		//release the file variable
		file.clear() ;

		return true ;
	}
}

bool Map::lossCondition( )
{
	for( int row = 0; row < m_height; ++row )
		for( int col = 0; col < m_width; ++col )
		{
			if( m_map[row][col] == '*' && !m_hidden[row][col] )
			{
				return true ;
			}
		}
		return false ;
}

//gets the specific map data
char Map::get( int x, int y )
{
	return m_map[y][x] ;
}

void Map::generateMap( )
{
	int y, x ;

	//initialize flags and tiles
	m_flagsAvailable = m_mineAmount ;
	m_safeTiles = m_width * m_height - m_mineAmount ;

	for( int i = 0; i < m_mineAmount; ++i )
	{
		//generate mines, making sure every mine finds a spot
		do
		{
			y = rand()%m_height ;
			x = rand()%m_width ;
		} while( m_map[y][x] == '*' ) ;

		//place mine
		m_map[y][x] = '*' ;
	}

	//initialize map tiles
	for( int row = 0; row < m_height; ++row )
		for( int col = 0; col < m_width; ++col )
		{
			m_hidden[row][col] = true ;
			m_flagged[row][col] = false ;

			if(m_map[row][col] == '.')
				searchAdj(col, row) ;
		}
}

void Map::clearMap()
{
	//returns map to default state
	for( int row = 0; row < m_height; ++row )
			for( int col = 0; col < m_width; ++col )
			{
				m_map[row][col] = '.' ;
			}
}

void Map::reveal(int x, int y)
{ 
	//turns off hidden
	m_hidden[y][x] = false ;

	//reveal all adjacent tiles if there are no mines around
	if( m_map[y][x] == '.' )
		revealAdj( x, y ) ;
	
	//if tile is not a mine, decrement amount of safe tiles left
	if( m_map[y][x] != '*' )
		m_safeTiles-- ;
}

void Map::revealAdj(int x, int y)
{
	if( x > 0 )
	{
		//left
		if( isHidden( x-1, y ) && !isFlagged( x-1, y ) )
			reveal( x-1, y ) ;

		//upleft
		if( y > 0 )
			if( isHidden( x-1, y-1 ) && !isFlagged( x-1, y-1 ) )
				reveal( x-1, y-1 ) ;

		//downleft
		if( y < m_height - 1 )
			if( isHidden( x-1, y+1 ) && !isFlagged( x-1, y+1 ) )
				reveal( x-1, y+1 ) ;
	}

	if( x < m_width - 1 )
	{
		//right
		if( isHidden( x+1, y ) && !isFlagged( x+1, y ) )
			reveal( x+1, y ) ;

		//upright
		if( y > 0 )
			if( isHidden( x+1, y-1 ) && !isFlagged( x+1, y-1 ) )
				reveal( x+1, y-1 ) ;

		//downright
		if( y < m_height - 1 )
			if( isHidden( x+1, y+1 ) && !isFlagged( x+1, y+1 ) )
				reveal( x+1, y+1 ) ;
	}

	if( y > 0 )
	{
		//up
		if( isHidden( x, y-1 ) && !isFlagged( x, y-1 ) )
			reveal( x, y-1 ) ;
	}

	if( y < m_height - 1 )
	{
		//down
		if( isHidden( x, y+1 ) && !isFlagged( x, y+1 ) )
			reveal( x, y+1 ) ;
	}
}

//when the game is lost, all mines are revealed
void Map::revealMines()
{
	for( int row = 0; row < m_height; ++row )
		for( int col = 0; col < m_width; ++col )
		{
			if( m_map[row][col] == '*' )
				reveal(col, row) ;
		}
}

//searches neighbor tiles
void Map::searchAdj( int x, int y )
{
	int numMines = 0 ;
	if( x > 0 )
	{
		if( m_map[y][x-1] == '*' )
				numMines++ ;
		if( y > 0 )
			if( m_map[y-1][x-1] == '*' )
				numMines++ ;
		if( y < m_height - 1 )
			if( m_map[y+1][x-1] == '*' )
				numMines++ ;
	}
	if( x < m_width - 1 )
	{
		if( m_map[y][x+1] == '*' )
				numMines++ ;
		if( y > 0 )
			if( m_map[y-1][x+1] == '*' )
				numMines++ ;
		if( y < m_height - 1)
			if( m_map[y+1][x+1] == '*' )
				numMines++ ;
	}

	if( y > 0 )
		if( m_map[y-1][x] == '*' )
				numMines++ ;

	if( y < m_height - 1 )
		if( m_map[y+1][x] == '*' )
				numMines++ ;

	//for ID, make a char into a number
	if( numMines > 0 )
		m_map[y][x] = '0' + numMines ;
}

//deletes the 2D map data safely
void Map::release( )
{
	if( m_hidden )
	{
		for( int row = 0; row < m_height; ++row )
			delete [] m_hidden[row] ;
		delete m_hidden ;
		m_hidden = 0 ;
	}
	if( m_flagged != NULL )
	{
		for( int row = 0; row < m_height; ++row )
			delete [] m_flagged[row] ;
		delete m_flagged ;
		m_flagged = 0 ;
	}
	if( m_map )
	{
		delete2Dmap( m_map, m_height ) ;
		m_height = 0 ;
		m_width = 0 ;
	}
}

int Map::getID(int x, int y)
{
	if( isFlagged(x, y) )
	{
		return FLAGGED ;
	}
	else if( isHidden(x, y) )
		return HIDDEN ;
	else if( m_map[y][x] == '*' )
		return MINE ;
	else if( m_map[y][x] == '.' )
		return NOTHING ;
	else
	{
		//finds the number char of the map position and returns it as an int
		char c = m_map[y][x] ;
		return atoi(&c) ;
	}
}